#ifndef __FINITE_STATE_MACHINE_H__
#define __FINITE_STATE_MACHINE_H__

#include "common.h"
#include <set>
#include <map>
#include <iterator>
#include <stdarg.h>

BEG_DEF_NAMESPACE_FSM

/**
 * FSM_STATE_CODE  
 *	-3 	: _STATE_INVALID
 *	-2	: _STATE_WAIT( DO NOT CHANGE THE CURRENT STATE )
 *	-1	: _STATE_FINISH
 *	>=0	: STATE_NORMAL
 */
#define _STATE_INVALID	-3
#define _STATE_WAIT	-2
#define _STATE_FINISH	-1
typedef int FSM_STATE_CODE ;
#define ISVALIDSTATE( STATE )	( STATE > _STATE_WAIT )

/**
 * FSM_EL_RESULT
 *	-2	: _EL_INVALID 	"something error occured in HandleEntry"
 *	-1	: _EL_WAIT	"can't entry the next state,
 *				stay in the current state continually "
 *	0	: _EL_OK	"the state can change into the next state"
 */
#define _EL_INVALID	-2
#define _EL_WAIT	-1
#define _EL_OK		0
typedef int FSM_EL_RESULT;

template <typename TO , typename TI> class FiniteStateMachine;
template <typename TO , typename TI> class FSMState {
	typedef std::set< FSM_STATE_CODE > FSM_STATE_CODE_SET;
public:
	FSMState();
	virtual ~FSMState();
	/** 
	 * @brief Build a state 
	 * 
	 * @param stateCode the state code of this state
	 * @param ... the children state of this state
	 */
	void BuildState( FSM_STATE_CODE stateCode , ... );
private:
	bool IsSubStateCode( FSM_STATE_CODE stateCode );
	virtual FSM_EL_RESULT HandleEntry( TO pUserData , TI pInputData ){ 
//		pUserData=pUserData ; 
//		pInputData=pInputData; 
		return _EL_OK;
	}
	virtual FSM_EL_RESULT HandleLeave( TO pUserData , TI pInputData ){
//		pUserData=pUserData ; 
//		pInputData=pInputData; 
		return _EL_OK;
	}
private:
	friend class FiniteStateMachine<TO,TI>;
	virtual FSM_STATE_CODE HandleAction(TO output , TI input )=0;
private:
	FSM_STATE_CODE mStateCode;
	FSM_STATE_CODE_SET mSubStateCodeSet;
};

template <typename TO , typename TI>
FSMState<TO,TI>::FSMState(){
	mStateCode = _STATE_INVALID;
}

template <typename TO , typename TI>
FSMState<TO,TI>::~FSMState(){
}

template <typename TO , typename TI>
void FSMState<TO,TI>::BuildState( FSM_STATE_CODE  stateCode , ... ){
	mStateCode = stateCode;

	va_list args;
	va_start(args, stateCode);
	while (1){
		FSM_STATE_CODE sCode = va_arg( args, FSM_STATE_CODE );
		/* -1 is signed the end of the states list */
		if( sCode <= -1 ){
			break;
		}
		mSubStateCodeSet.insert( sCode );
	}
	va_end(args);
	return ;
}

template <typename TO , typename TI>
bool FSMState<TO,TI>::IsSubStateCode( FSM_STATE_CODE stateCode ){
	return (mSubStateCodeSet.find(stateCode) != mSubStateCodeSet.end());
}

/*************** Defination of FiniteStateMachine **********/
/* The Followint is the return value of numbers of functions of state_machine */
#define OK			0 /* ok */
#define WAIT			1 /* wait */
#define ERR			2 /* general error,mostly to be used for checking the return value */
#define ERR_STATE_DUPLICATE	3 /* already exist the same state code */
#define ERR_STATE_NOTEXIST 	4 /* the specific state code don't exist*/
#define ERR_STATE_INVALID	5 /* the mCurrStateCode is invalid */
#define ERR_NOT_SUB_STATE	6 /* the required state is not the child state of current state */
#define ERR_STATE_ENTRY		7 /* the entry function of state return false */
#define ERR_STATE_LEAVE		8 /* the leave function of state return false */
#define ERR_STATE_ACTION_FAILED 9 /* the return value of action function is _STATE_INVALID */


template< typename TO , typename TI >
class FiniteStateMachine{
	typedef std::map< FSM_STATE_CODE , FSMState<TO,TI> * > FSM_STATE_MAP;
public:
	FiniteStateMachine();
	~FiniteStateMachine();
	/** 
	 * @brief trigger the state machine to run 
	 * 
	 * @param output the private data of user for changing (sometimes can also contain the materials )
	 * @param input the input data (the materials of state machine or additional data or nothing )
	 * 
	 * @return 
	 */
	int Trigger( TO output , TI input );
	int BuildFSM(FSM_STATE_CODE beginStateCode , FSM_STATE_CODE endStateCode , ... );

private:
	int CheckState( FSM_STATE_CODE stateCode , FSMState<TO,TI> ** ppState);
	FSMState<TO,TI> * FindState( FSM_STATE_CODE stateCode );
private:
//	FSM_STATE_CODE mCurrStateCode;
	FSM_STATE_CODE mBeginStateCode;
	FSM_STATE_CODE mEndStateCode;

	FSM_STATE_MAP  mStateMap;

	FSMState<TO,TI> * mpPreviousState;
	FSMState<TO,TI> * mpCurrentState;

};

#define CreateFSMState( TYPE , VARIABLE , STATECODE , ... ) \
	 TYPE *VARIABLE = new TYPE();VARIABLE->BuildState( STATECODE ,__VA_ARGS__)

template <typename TO, typename TI>
FiniteStateMachine<TO,TI>::FiniteStateMachine(){
//	mCurrStateCode	= _STATE_INVALID;
	mBeginStateCode	= _STATE_INVALID;
	mEndStateCode	= _STATE_INVALID;
	mpPreviousState = NULL;
	mpCurrentState = NULL;
}
template <typename TO, typename TI>
FiniteStateMachine<TO,TI>::~FiniteStateMachine(){
	typename FSM_STATE_MAP::iterator iter;
	for(iter = mStateMap.begin();
	    iter != mStateMap.end();
	    iter++ ){
		delete iter->second;
	}
}
template <typename TO,typename TI>
FSMState<TO,TI> * FiniteStateMachine<TO,TI>::FindState( FSM_STATE_CODE stateCode ){
	typename FSM_STATE_MAP::iterator iter = mStateMap.find( stateCode );
	iter = mStateMap.find( stateCode );
	if( iter == mStateMap.end() ){
		return NULL;
	}
	return iter->second;
}

template <typename TO , typename TI >
int FiniteStateMachine<TO,TI>::CheckState( FSM_STATE_CODE stateCode , FSMState<TO,TI> ** ppState ){
	/* check if the state code is valid*/
	if( !ISVALIDSTATE( stateCode ) ){
		return ERR_STATE_INVALID;
	}

	/* check if the stateCode is exist */
	FSMState<TO,TI> * ptmpState = NULL;
	if( ( ptmpState = FindState(stateCode)) == NULL  ){
		return ERR_STATE_NOTEXIST; 
	}
	if( ppState ){
		*ppState = ptmpState;
	}
	return OK;
}


template <typename TO, typename TI>
int FiniteStateMachine<TO,TI>::BuildFSM( FSM_STATE_CODE beginStateCode , 
					 FSM_STATE_CODE endStateCode , ... ){


	va_list args;
	int retVal = OK ;

	FSM_STATE_CODE tmpStateCode;
	FSMState<TO,TI> * ptmpState;

	/* choose one of the following line to avoid the failed 
	 * occured in the macro(va_arg) expansion produces */
	typedef FSMState<TO,TI> * FSMPtr;

	va_start(args, endStateCode);
	while (1){
		ptmpState = va_arg( args, FSMPtr );
		if( ptmpState == NULL){
			break;
		}
		tmpStateCode = ptmpState->mStateCode;

		/* there must be unique object per state code in the state code map */
		if( (retVal = CheckState( tmpStateCode, NULL)) != ERR_STATE_NOTEXIST ){
			return  retVal == OK?ERR_STATE_DUPLICATE:retVal;
		}

		mStateMap.insert( std::make_pair(tmpStateCode , ptmpState) );
		if( tmpStateCode == endStateCode ){
			break;
		}
	}
	va_end(args);

	/* check the start state */
	if( (retVal=CheckState( beginStateCode , &mpCurrentState)) != OK ){
		return retVal;
	}
	mBeginStateCode = beginStateCode;
//	mCurrStateCode = beginStateCode;

	mpPreviousState = NULL;

	/* check the end state */
	if( (retVal = CheckState( endStateCode , NULL)) != OK ){
		return retVal;
	}
	mEndStateCode = endStateCode ;

	return OK;
}

template <typename TO , typename TI>
int FiniteStateMachine<TO,TI>::Trigger( TO userData , TI pData ){
	int retVal;
	bool stateRotation =false;
	FSM_STATE_CODE tmpStateCode = _STATE_INVALID;
	FSMState<TO,TI> * pTempState = NULL;

	while(1){
		if( stateRotation && mpPreviousState ){
			stateRotation = false;
			retVal = mpPreviousState->HandleLeave( userData , pData );
			switch( retVal ){
			case _EL_OK:
				break;
			case _EL_WAIT:
			case _EL_INVALID:
			default:
				return ERR_STATE_LEAVE;
			}

		}
	
		if( NULL == mpCurrentState ){
//			if( (retVal = CheckState( mCurrStateCode , &mpCurrentState )) != OK ){
				return ERR_STATE_INVALID;
//			}
		}
	
		/*check if the returned state code is the sub state of current state */
		tmpStateCode = mpCurrentState->HandleAction(userData, pData );
		switch(tmpStateCode){
		case _STATE_INVALID:
			return ERR_STATE_ACTION_FAILED; 
		case _STATE_WAIT:
			return WAIT;
		case _STATE_FINISH:
			return OK;
		default:
			if( !mpCurrentState->IsSubStateCode(tmpStateCode) ){
				return ERR_NOT_SUB_STATE;
			}
			
			if( (retVal = CheckState( tmpStateCode, &pTempState )) != OK ){
				return retVal;
			}

			retVal = pTempState->HandleEntry( userData, pData );
			switch( retVal ){
			case _EL_OK:
				break;
			case _EL_WAIT:
				return WAIT;
			case _EL_INVALID:
			default:
				return ERR_STATE_ENTRY;
			}
			mpPreviousState = mpCurrentState;

//			mCurrStateCode = tmpStateCode;
			mpCurrentState = pTempState;
			stateRotation = true;
//			return OK;

		}
	}
}

END_DEF_NAMESPACE_FSM

#endif
